/* typehints:start */
import { Application } from "../../application";
import { ModalDialogs } from "../utils/modal_dialog";
/* typehints:end */

import { createLogger } from "../utils/logging";

const logger = createLogger('setting_type')

export class BaseSetting{
  constructor(id, categoryId, changeCb, enabledCb = null){
    this.id = id;
    this.categoryId = categoryId;
    this.changeCb = changeCb;
    this.enabledCb = enabledCb;

    /** @type {Application} */
    this.app = null;

    this.element = null;
    this.dialogs = null;
  }

  /**
   * 
   * @param {Application} app 
   * @param {*} value 
   */
  apply(app, value){
    if(this.changeCb){
      this.changeCb(app, value);
    }
  }

  bind(app, element, dialogs){
    this.app = app;
    /** @type {HTMLElement} */
    this.element = element;
    /** @type {ModalDialogs} */
    this.dialogs = dialogs;
  }

  getHtml(app){
    abstract;
    return "";
  }

  getIsAvailable(app){
    return this.enabledCb ? this.enabledCb(app) : true;
  }

  syncValueToElement(){
    abstract;
  }

  modify(){
    abstract;
  }

  showRestartRequiredDialog(){
    const { restart } = this.dialogs.showInfo(
      '需要重启游戏',
      '您需要重启游戏来应用变更的设置',
      ['later:grey:以后', 'restart:misc:重启']
    )

    if(restart){
      restart.add(() => this.app.platform.restart());
    }
  }

  validate(value){
    abstract;
    return false;
  }
}

export class EnumSetting extends BaseSetting{
  constructor(
    id,
    {
      label,
      description,
      options,
      valueGetter,
      textGetter,
      descGetter = null,
      category,
      restartRequired = true,
      changeCb = null,
      magicValue = null,
      enabledCb = null,
    }
  ){
    super(id, category, changeCb, enabledCb);

    this.label = label;
    this.description = description;    
    this.options = options;
    this.valueGetter = valueGetter;
    this.textGetter = textGetter;
    this.descGetter = descGetter || (() => null);
    this.restartRequired = restartRequired;
    this.magicValue = magicValue;
  }

  getHtml(app){
    const available = this.getIsAvailable(app);
    return `
      <div class="setting cardbox ${available ? 'enabled' : 'disabled'}">
        ${available ? "" : `<span class="onlyHint">暂不可用</span>`}
        <div class="row">
          <label>${this.label}</label>
          <div class="value enum" data-setting="${this.id}"></div>
        </div> 
        <div class="desc">
          ${this.description}
        </div>
      </div>
    `
  }

  validate(value){
    if(value === this.magicValue){
      return true;
    }

    const availableValues = this.options.map(option => this.valueGetter(option));
    if(availableValues.indexOf(value) < 0){
      logger.error(`设置<${this.id}>值错误: ${value}`);
      return false;
    }

    return true;
  }

  syncValueToElement(){
    const value = this.app.setting.getSetting(this.id);
    let displayText = '???';
    const matchedInstance = this.options.find(data => this.valueGetter(data) === value);
    if(matchedInstance){
      displayText = this.textGetter(matchedInstance);
    } else {
      logger.warn('Setting value', value, 'not found for', this.id, '!');
    }
    this.element.innerText = displayText;
  }

  modify(){
    const { optionSelected } = this.dialogs.showOptionChooser(this.label, {
      active: this.app.setting.getSetting(this.id),
      options: this.options.map(option => ({
        value: this.valueGetter(option),
        text: this.textGetter(option),
        desc: this.descGetter(option),
      }))
    });

    optionSelected.add(value => {
      this.app.setting.updateSetting(this.id, value);
      this.syncValueToElement();

      if(this.restartRequired){
        this.showRestartRequiredDialog();
      }
    }, this);
  }
}

export class BoolSetting extends BaseSetting{
  constructor(id, label, description, category, changeCb = null, enabledCb = null){
    super(id, category, changeCb, enabledCb)

    this.label = label;
    this.description = description;
  }

  getHtml(app){
    const available = this.getIsAvailable(app);
    return `
    <div class="setting cardbox ${available ? 'enabled' : 'disabled'}">
      ${available ? "" : `<span class="onlyHint">暂不可用</span>`}

      <div class="row">
        <label>${this.label}</label>
        <div class="value checkbox checked" data-setting="${this.id}">
          <span class="knob"></span>
        </div>
      </div>
      <div class="desc">
        ${this.description}
      </div>
    </div>
    `
  }

  syncValueToElement(){
    const value = this.app.setting.getSetting(this.id);
    this.element.classList.toggle('checked', value);
  }

  modify(){
    const newValue = !this.app.setting.getSetting(this.id);
    this.app.setting.updateSetting(this.id, newValue);
    this.syncValueToElement();
  }

  validate(value){
    return typeof value === 'boolean'
  }
}

export class RangeSetting extends BaseSetting{
  constructor(
    id,
    label,
    description,
    category,
    changeCb = null,
    defaultValue = 1.0,
    minValue = 0,
    maxValue = 1.0,
    stepSize = 0.0001,
    enabledCb = null
  ){
    super(id, category, changeCb, enabledCb);

    this.label = label;
    this.description = description;
    this.defaultValue = defaultValue;
    this.minValue = minValue;
    this.maxValue = maxValue;
    this.stepSize = stepSize;
  }

  getHtml(app){
    const available = this.getIsAvailable(app);
    return `
    <div class="setting cardbox ${available ? 'enabled' : 'disabled'}">
      ${available ? '' : `<span class="onlyHint">暂不可用</span>`}

      <div class="row">
        <label>${this.label}</label>
        <div class="value rangeInputContainer noPressEffect" data-setting="${this.id}">
          <label>${this.defaultValue}</label>
          <input class="rangeInput" type="range" value="${this.defaultValue}" min="${this.minValue}" max="${this.maxValue}" step="${this.stepSize}">
        </div>
      </div>
      <div class="desc">
        ${this.description}
      </div>
    </div>
    `
  }

  bind(app, element, dialogs){
    this.app = app;
    this.element = element;
    this.dialogs = dialogs;

    this.getRangeInputElement().addEventListener('input', () => {
      this.updateLabels();
    })

    this.getRangeInputElement().addEventListener('change', () => {
      this.modify();
    }) 
  }

  syncValueToElement(){
    const value = this.app.setting.getSetting(this.id)
    this.setElementValue(value);
  }

  setElementValue(value){
    const rangeInput = this.getRangeInputElement();
    const rangeLabel = this.element.querySelector('label')
    rangeInput.value = String(value);
    rangeLabel.innerHTML = String(Math.round(value * 100.0)) + '%';
  }

  updateLabels(){
    const value = Number(this.getRangeInputElement().value)
    this.setElementValue(value)
  }

  getRangeInputElement(){
    return this.element.querySelector('input.rangeInput');
  }

  modify(){
    const rangeInput = this.getRangeInputElement();
    const newValue = Math.round(Number(rangeInput.value) * 100.0) / 100.0;
    this.app.setting.updateSetting(this.id, newValue);
    this.syncValueToElement();
    console.log("SET", newValue)
  }

  validate(value){
    return typeof value === 'number' && value >= this.minValue && value <= this.maxValue;
  }
}